Annale · session normale 2020-2021 · ING2 MF
Examen C++ 2020 — 2021 · MF
Un gros sujet de programmation (12 pts) : simuler un ordonnanceur de processus avec priority_queue STL, foncteur de comparaison, héritage depuis une classe abstraite Objet, saisie sécurisée par exceptions.
i. Le sujet
ii. Notions mobilisées
| Bloc | Notion clé | Chapitre |
|---|---|---|
Classe abstraite Objet + afficher() virtuelle pure | Polymorphisme dynamique, classes abstraites | SE4 §7 |
Héritage Processus / Ordonnanceur | Hiérarchie, mot-clé public, redéfinition | SE4 |
priority_queue<Processus> + foncteur | STL, container associatif, operator() | SE7 |
| Saisie sécurisée par exceptions | try/catch/throw | SE6 |
Affichage avec sleep() de <unistd.h> | I/O console, temporisation système | SE5 |
iii. Repères de correction
Architecture demandée — 4 classes
Hiérarchie
Objet: classe abstraite avecvirtual void afficher() = 0;Processus: hérite d'Objet, redéfinitafficher(). Attributsid,duree,priorityOrdonnanceur: hérite d'Objet, contient unpriority_queue<Processus, vector<Processus>, Comparaison>Comparaison: foncteur fourni avecbool operator()(const Processus&, const Processus&)
Pourquoi Objet abstrait avec afficher() virtuelle pure ?
L'énoncé demande la justification. Réponse type :
- L'interface commune
afficher()permet de manipuler de manière uniforme les objets héritiers (ProcessusetOrdonnanceur) à travers un pointeur surObjet. - Le rendre virtuelle pure impose à chaque classe dérivée de définir son propre
afficher()— c'est un contrat de polymorphisme dynamique. La classeObjetne peut pas être instanciée seule, ce qui empêche les erreurs d'usage.
Classe Processus
// processus.h
#include "objet.h"
class Processus : public Objet {
public:
int id;
int duree;
int priority;
Processus(int _id, int _duree, int _prio)
: id(_id), duree(_duree), priority(_prio) { }
virtual void afficher() override {
cout << "P(" << id << ", d=" << duree
<< ", prio=" << priority << ")";
}
};
Classe Ordonnanceur
// ordonnanceur.h
#include <queue>
#include <vector>
#include "processus.h"
class Comparaison {
public:
bool operator()(const Processus& p1, const Processus& p2) {
return p1.priority < p2.priority; // plus grand prio = plus prioritaire
}
};
class Ordonnanceur : public Objet {
public:
priority_queue<Processus, vector<Processus>, Comparaison> pq;
int quantum = 100; // ms
void ajouter(const Processus& p) { pq.push(p); }
void traiter() {
while (!pq.empty()) {
Processus p = pq.top(); pq.pop();
cout << "Traitement de "; p.afficher();
cout << endl;
p.duree -= quantum;
if (p.priority > 1) p.priority--;
sleep(1); // temporisation visuelle
if (p.duree > 0) pq.push(p); // remettre dans la file
else cout << "P(" << p.id << ") terminé\n";
}
}
virtual void afficher() override {
cout << "Ordonnanceur (" << pq.size() << " processus)";
}
};
Saisie sécurisée avec exceptions
int saisirInt(const string& msg, int min, int max) {
int a;
while (true) {
try {
cout << msg << " [" << min << ", " << max << "] : ";
cin >> a;
if (cin.fail()) { cin.clear(); cin.ignore(1000, '\n'); throw 10; }
if (a < min || a > max) throw 20;
return a;
} catch (int) {
cout << "Erreur de saisie, recommence.\n";
}
}
}
Programme principal
#include <iostream>
#include <unistd.h> // pour sleep()
#include "ordonnanceur.h"
using namespace std;
int main(int argc, char** argv) {
if (argc != 2) {
cerr << "Usage : " << argv[0] << " nbProcessus" << endl;
return 1;
}
int nb = atoi(argv[1]);
Ordonnanceur ord;
for (int i = 0; i < nb; i++) {
cout << "\n--- Processus #" << (i+1) << " ---\n";
int duree = saisirInt("durée (ms)", 100, 10000);
int prio = saisirInt("priorité", 1, 10);
ord.ajouter(Processus(i+1, duree, prio));
}
ord.traiter();
return 0;
}
Compilation : g++ *.cpp -Wall -o ordonnanceur · Exécution : ./ordonnanceur 4.
Détails à ne pas oublier
- L'énoncé précise que la priorité décroît de 1 unité à chaque traitement, jusqu'à 1 (puis reste constante). Cela simule un mécanisme de nice Unix.
- Quand
duree ≤ 0, le processus est supprimé de la file (pas remis). priority_queueavec un foncteuroperator()qui comparep1.priority < p2.prioritymettra le plus grand en tête (max-heap).sleep(1)(Linux) ouSleep(1000)(Windows) entre chaque traitement pour bien suivre la simulation.
★ Notions ré-activées
- Classe abstraite :
virtual T m() = 0; - Héritage public : la classe fille hérite de l'interface, redéfinit les virtuelles
priority_queue<T, vector<T>, Cmp>: max-heap par défaut- Foncteur de comparaison : classe avec
bool operator()(const T&, const T&) - Méthodes principales :
push,top,pop,empty,size - Saisie sécurisée : boucle
try/catchavecthrowsur erreur cin.fail()+cin.clear()+cin.ignore()pour reset le flux après erreursleep(s)Linux /Sleep(ms)Windows depuis<unistd.h>ou<windows.h>- Compilation multi-fichiers :
g++ *.cpp -Wall -o exec